package com.zzh.custom;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewTreeObserver;

import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class ShapeDrawView extends View {

    private static String TAG = ShapeDrawView.class.getSimpleName();

    // 点的集合
    private List<PointF> mPoints = new ArrayList<>();
    // 整个view区域的地区
    private Region mAllViewRegion = new Region();
    // 画线的paint
    private Paint mLinePaint;
    // 画点的paint
    private Paint mPointPaint;
    // 片区的颜色paint
    private Paint mAreaPaint;
    // 点的半径
    private int mPointRadius = 20;
    // 片区颜色的path
    private Path mColorPath = new Path();
    // 点的点击区域的地区
    private List<Region> mPointRegions = new ArrayList<>();
    // 回退栈
    private Stack<StepBean> mBackStack = new Stack<>();
    // 中心点
    private PointF mCenterPointF = new PointF(-1f, -1f);
    // 缩放比
    private float mZoom = 13;

    // 初始宽高
    private int mWidth;
    private int mHeight;


//    // 前进栈
//    private Stack<StepBean> mNextStack = new Stack<>();

    private Paint mTestPath;
    // 手势判定限制值
    private int mScaledTouchSlop;


    public ShapeDrawView(Context context) {
        this(context, null);
    }

    public ShapeDrawView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, -1);
    }

    public ShapeDrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        mScaledTouchSlop = ViewConfiguration.get(context).getScaledWindowTouchSlop();
        createPoints();
        initPaint();
    }

    private float mOldScalePerPixel = 0f;

    public void initData(float scalePerPixel) {
        mOldScalePerPixel = scalePerPixel;
    }

    private void initPaint() {
        mLinePaint = new Paint();
        mLinePaint.setColor(Color.RED);
        mLinePaint.setAntiAlias(true);
        mLinePaint.setStrokeWidth(2);

        mPointPaint = new Paint();
        mPointPaint.setColor(Color.BLACK);
        mPointPaint.setAntiAlias(true);
        mPointPaint.setStrokeWidth(10);

        mTestPath = new Paint();
        mTestPath.setColor(Color.parseColor("#5500ff00"));
        mTestPath.setAntiAlias(true);
        mTestPath.setStrokeWidth(10);

        mAreaPaint = new Paint();
        mAreaPaint.setColor(Color.parseColor("#55ff0000"));
        mAreaPaint.setAntiAlias(true);
    }

    private void createPoints() {
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getViewTreeObserver().removeOnGlobalLayoutListener(this::onGlobalLayout);
                int width = getWidth();
                int height = getHeight();
                if (mWidth == 0) {
                    if (width > 0) {
                        mWidth = width;
                        mHeight = height;

                        if (mCenterPointF.x == -1 && mCenterPointF.y == -1) {
                            mCenterPointF.set(mWidth / 2f, mHeight / 2f);
                        }

                        float widthQuarter = width / 4f;
                        float heightQuarter = height / 4f;

                        mPoints.clear();
                        mPoints.add(new PointF(mCenterPointF.x - widthQuarter, mCenterPointF.y - heightQuarter));
                        mPoints.add(new PointF(mCenterPointF.x + widthQuarter, mCenterPointF.y - heightQuarter));
                        mPoints.add(new PointF(mCenterPointF.x + widthQuarter, mCenterPointF.y + heightQuarter));
                        mPoints.add(new PointF(mCenterPointF.x - widthQuarter, mCenterPointF.y + heightQuarter));

                        updateInvalidate();
                    }
                }
            }
        });
    }

    private boolean mIsResetRegion = true;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int allSize = mPoints.size();
        if (allSize < 3) {
            for (Region pointRegion : mPointRegions) {
                pointRegion.setEmpty();
            }
            for (Region lineRegion : mLineRegions) {
                lineRegion.setEmpty();
            }
            return;
        }

        // 点击区域限制重置
        if (mIsResetRegion) {

            resetRegion();
            resetLineRegion();
            mIsResetRegion = false;
        }

        // 绘制区间颜色
        mColorPath.reset();
        for (int i = 0; i < mPoints.size(); i++) {
            PointF point = mPoints.get(i);
            if (i == 0) {
                mColorPath.moveTo(point.x, point.y);
            } else {
                mColorPath.lineTo(point.x, point.y);
            }
        }
        mColorPath.close();
        canvas.drawPath(mColorPath, mAreaPaint);

        // 绘制线条
        for (int i = 1; i < mPoints.size(); i++) {
            PointF pre = mPoints.get(i - 1);
            PointF now = mPoints.get(i);
            canvas.drawLine(pre.x, pre.y, now.x, now.y, mLinePaint);
            if (i == allSize - 1) {
                pre = now;
                now = mPoints.get(0);
                canvas.drawLine(pre.x, pre.y, now.x, now.y, mLinePaint);
            }
        }

        // 绘制点
        for (PointF point : mPoints) {
            canvas.drawCircle(point.x, point.y, mPointRadius, mPointPaint);
        }

        // test
//        // 绘制path
//        for (int i = 0; i < mPoints.size(); i++) {
//            if (mLineAuxiliaryPath.size() > i) {
//                Path path = mLineAuxiliaryPath.get(i);
//                canvas.drawPath(path, mTestPath);
//            }
//        }
//        // 绘制中心点
//        canvas.drawCircle(mCenterPointF.x, mCenterPointF.y, 30, mPointPaint);
    }

    private void resetRegion() {
        for (int i = 0; i < mPoints.size(); i++) {
            PointF point = mPoints.get(i);
            Region region;
            if (mPointRegions.size() > i) {
                region = mPointRegions.get(i);
            } else {
                region = new Region();
                mPointRegions.add(region);
            }
            region.setEmpty();
            region.set((int) (point.x - mPointRadius), (int) (point.y - mPointRadius),
                    (int) (point.x + mPointRadius), (int) (point.y + mPointRadius));
        }
    }

    private List<Region> mLineRegions = new ArrayList<>();
    private List<Path> mLineAuxiliaryPath = new ArrayList<>();

    private void resetLineRegion() {
        for (int i = 1; i < mPoints.size(); i++) {
            PointF prePoint = mPoints.get(i - 1);
            PointF nowPoint = mPoints.get(i);
            updateSingleLineRegion(i - 1, prePoint, nowPoint);
            if (i == mPoints.size() - 1) {
                prePoint = nowPoint;
                nowPoint = mPoints.get(0);
                updateSingleLineRegion(i, prePoint, nowPoint);
            }
        }
    }

    private void updateSingleLineRegion(int nowIndex, PointF prePoint, PointF nowPoint) {

        Region region;
        Path auxiliaryPath;
        if (mLineRegions.size() > nowIndex) {
            region = mLineRegions.get(nowIndex);
            auxiliaryPath = mLineAuxiliaryPath.get(nowIndex);
        } else {
            region = new Region();
            mLineRegions.add(region);
            auxiliaryPath = new Path();
            mLineAuxiliaryPath.add(auxiliaryPath);
        }

        configParallelLinesPath(auxiliaryPath, prePoint, nowPoint);

        region.setEmpty();
        region.setPath(auxiliaryPath, mAllViewRegion);

    }

    private void configParallelLinesPath(Path path, PointF prePoint, PointF nowPoint) {
        path.reset();
        int offset = mPointRadius * 2;
        // 象限角度
        int quadrantAngle = getAngle(prePoint.x, prePoint.y, nowPoint.x, nowPoint.y);
        //
        float quadrantAngleTo90 = quadrantAngle % 90;
        double sinValueOne = Math.sin(quadrantAngleTo90 / 180 * Math.PI);
        double sinValueTwo = Math.sin((90 - quadrantAngleTo90) / 180 * Math.PI);

        int a = Math.abs((int) (sinValueOne * offset));
        int b = Math.abs((int) (sinValueTwo * offset));

        // 所有象限基于 prePoint 点
        if (quadrantAngle == 0 || quadrantAngle == 180) {
            path.moveTo(prePoint.x - offset, prePoint.y);
            path.lineTo(prePoint.x + offset, prePoint.y);
            path.lineTo(nowPoint.x + offset, nowPoint.y);
            path.lineTo(nowPoint.x - offset, nowPoint.y);
        } else if (quadrantAngle == 90 || quadrantAngle == 270) {
            path.moveTo(prePoint.x, prePoint.y - offset);
            path.lineTo(prePoint.x, prePoint.y + offset);
            path.lineTo(nowPoint.x, nowPoint.y + offset);
            path.lineTo(nowPoint.x, nowPoint.y - offset);
        } else if (quadrantAngle < 90) {
            // 第一象限
            path.moveTo(prePoint.x - b, prePoint.y - a);
            path.lineTo(prePoint.x + b, prePoint.y + a);
            path.lineTo(nowPoint.x + b, nowPoint.y + a);
            path.lineTo(nowPoint.x - b, nowPoint.y - a);
        } else if (quadrantAngle < 180) {
            // 第二象限
            path.moveTo(prePoint.x - a, prePoint.y + b);
            path.lineTo(prePoint.x + a, prePoint.y - b);
            path.lineTo(nowPoint.x + a, nowPoint.y - b);
            path.lineTo(nowPoint.x - a, nowPoint.y + b);
        } else if (quadrantAngle < 270) {
            // 第三象限
            path.moveTo(prePoint.x - b, prePoint.y - a);
            path.lineTo(prePoint.x + b, prePoint.y + a);
            path.lineTo(nowPoint.x + b, nowPoint.y + a);
            path.lineTo(nowPoint.x - b, nowPoint.y - a);
        } else {
            // 第四象限
            path.moveTo(prePoint.x - a, prePoint.y + b);
            path.lineTo(prePoint.x + a, prePoint.y - b);
            path.lineTo(nowPoint.x + a, nowPoint.y - b);
            path.lineTo(nowPoint.x - a, nowPoint.y + b);
        }
        path.close();
    }

    /**
     * 获取相对于坐标系的夹角度数
     *
     * @param preX
     * @param preY
     * @param nowX
     * @param nowY
     * @return
     */
    private int getAngle(float preX, float preY, float nowX, float nowY) {
        if (nowX == preX) {
            // 竖直
            if (nowY == preY) {
                return 0;
            } else if (nowY > preY) {
                return 180;
            } else {
                return 0;
            }
        } else if (nowY == preY) {
            // 水平
            if (nowX > preX) {
                return 90;
            } else {
                return 270;
            }
        }
//        float x = Math.abs(nowX - preX);
//        float y = Math.abs(nowY - preY);
//        double tan = Math.atan2(x, y);
//        double angleA = 180 * tan /Math.PI;
//
//        if (nowX > preX) {
//            // 一二 象限
//            if (nowY > preY) {
//                // 二象限
//            } else {
//                // 一象限
//            }
//        } else {
//            // 三四 象限
//            if (nowY > preY) {
//                // 三象限
//            } else {
//                // 四象限
//            }
//        }


        double rotation = 0;

        double k1 = (double) (preY - preY) / (preX * 2 - preX);
        double k2 = (double) (nowY - preY) / (nowX - preX);
        double tmpDegree = Math.atan((Math.abs(k1 - k2)) / (1 + k1 * k2)) / Math.PI * 180;

        if (nowX > preX && nowY < preY) {  //第一象限
            rotation = 90 - tmpDegree;
        } else if (nowX > preX && nowY > preY) //第二象限
        {
            rotation = 90 + tmpDegree;
        } else if (nowX < preX && nowY > preY) { //第三象限
            rotation = 270 - tmpDegree;
        } else if (nowX < preX && nowY < preY) { //第四象限
            rotation = 270 + tmpDegree;
        } else if (nowX == preX && nowY < preY) {
            rotation = 0;
        } else if (nowX == preX && nowY > preY) {
            rotation = 180;
        }

        return (int) rotation;
    }

    @Override
    public boolean onFilterTouchEventForSecurity(MotionEvent event) {
        return super.onFilterTouchEventForSecurity(event);
    }

    // true时 point在操作 false 没有
    private boolean mIsPointClick = false;
    // 操作的point点的index值
    private int mClickPointIndex = -1;
    // 是不是允许手势监听
    private boolean mIsAllowTouch = true;

    // 按在的 x y 值
    private int mDownX;
    private int mDownY;
    // 添加或者移动点的type 1 移动 2 添加
    private int mAddOrMove = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction() & MotionEvent.ACTION_MASK) {
            case MotionEvent.ACTION_POINTER_DOWN:
                if (Math.abs(event.getX() - mDownX) < mScaledTouchSlop && Math.abs(event.getY() - mDownY) < mScaledTouchSlop) {
                    mIsPointClick = false;
                    return super.onTouchEvent(event);
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                Log.i(TAG, "onTouchEvent: position up");
                break;
            case MotionEvent.ACTION_DOWN:
                if (!mIsAllowTouch) {
                    mIsPointClick = false;
                    return super.onTouchEvent(event);
                }
                mDownX = (int) event.getX();
                mDownY = (int) event.getY();
                for (int i = 0; i < mPoints.size(); i++) {
                    Region region = mPointRegions.get(i);
                    if (region.contains(mDownX, mDownY)) {
//                        mBackStack.push(createStepBean(i, mPoints.get(i), StepBean.OPERATION_POINT));
                        mIsPointClick = true;
                        mAddOrMove = 1;
                        mClickPointIndex = i;
                        return true;
                    }
                }
                mIsPointClick = false;
                for (int i = 0; i < mPoints.size(); i++) {
                    Region region = mLineRegions.get(i);
                    if (region.contains(mDownX, mDownY)) {
//                        mPoints.add(i + 1, new PointF(mDownX, mDownY));
//                        mBackStack.push(createStepBean(i + 1, null, StepBean.OPERATION_LINE));
                        mIsPointClick = true;
                        mAddOrMove = 2;
                        mClickPointIndex = i + 1;
                        return true;
                    }
                }
                mIsPointClick = false;
                break;
            case MotionEvent.ACTION_UP:
                if (mIsPointClick) {
                    updateInvalidate();
                    mIsPointClick = false;
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                if (mIsPointClick) {
                    if (Math.abs(event.getX() - mDownX) < mScaledTouchSlop && Math.abs(event.getY() - mDownY) < mScaledTouchSlop) {
                        return super.onTouchEvent(event);
                    }
                    if (mAddOrMove == 1) {
                        mBackStack.push(createStepBean(mClickPointIndex, mPoints.get(mClickPointIndex), StepBean.OPERATION_POINT));
                        mAddOrMove = 0;
                    } else if (mAddOrMove == 2) {
                        mPoints.add(mClickPointIndex, new PointF(event.getX(), event.getY()));
                        mBackStack.push(createStepBean(mClickPointIndex, null, StepBean.OPERATION_LINE));
                        mAddOrMove = 0;
                    }
                    // 绘制边界问题
                    if (!isAllowTouchDrawBorder(event)) {
                        return true;
                    }
                    // 点的移动
                    updateOperationPoint(event, mClickPointIndex);
                    return true;
                }
                break;
        }
        return mIsPointClick ? true : super.onTouchEvent(event);
    }

    public void setIsAllowTouch(boolean mIsAllowTouch) {
        this.mIsAllowTouch = mIsAllowTouch;
    }

    public boolean isIsAllowTouch() {
        return mIsAllowTouch;
    }

    private StepBean createStepBean(int index, PointF point, int operationType) {
        StepBean stepBean = new StepBean();
        stepBean.setIndex(index);
        if (point != null) {
            stepBean.setPoint(new PointF(point.x, point.y));
        }
        stepBean.setOperationType(operationType);
        return stepBean;
    }

    // 判断是不是允许拖动到达的点
    // 这里包含上下左右的的padding值 不做常量话 直接在这里修改就好
    private boolean isAllowTouchDrawBorder(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();

        if (x < 20) {
            return false;
        }

        if (y < 20) {
            return false;
        }

        if (getWidth() - x < 20) {
            return false;
        }

        if (getHeight() - y < 20) {
            return false;
        }

        return true;
    }

    // 修改指定point的值 并重绘
    private void updateOperationPoint(MotionEvent event, int index) {
        PointF point = mPoints.get(index);
        point.x = event.getX();
        point.y = event.getY();
        invalidate();
    }

    // 回退到上一步
    public int previous() {
        if (mBackStack.size() > 0) {
            StepBean stepBean = mBackStack.pop();
            int index = stepBean.getIndex();
            switch (stepBean.getOperationType()) {
                case StepBean.OPERATION_LINE:
                    mPoints.remove(index);
                    break;
                case StepBean.OPERATION_POINT:
                    PointF point = mPoints.get(index);
                    PointF stepPoint = stepBean.getPoint();
                    if (point.x == stepPoint.x && point.y == stepPoint.y) {
                        return previous();
                    }
                    point.x = stepPoint.x;
                    point.y = stepPoint.y;
                    break;
            }
            updateInvalidate();
            return index;
        }
        return -1;
    }

    // 修改布局 同步修改点击区域
    private void updateInvalidate() {
        mAllViewRegion.set(0, 0, mWidth, mHeight);
        mIsResetRegion = true;
        invalidate();
    }

    // 是不是还有可以回退的点
    public boolean isPrevious() {
        if (mBackStack.size() <= 0) {
            return false;
        }
        StepBean stepBean = mBackStack.peek();
        if (stepBean.getOperationType() == StepBean.OPERATION_POINT) {
            PointF point = mPoints.get(stepBean.getIndex());
            PointF stepPoint = stepBean.getPoint();
            if (point.x == stepPoint.x && point.y == stepPoint.y) {
                mBackStack.pop();
                return isPrevious();
            }
        }
        return true;
    }


    // 修改缩放率
    public void updateSize() {
        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                getViewTreeObserver().removeOnGlobalLayoutListener(this::onGlobalLayout);
                if (mWidth == getWidth() && mHeight == getHeight()) {
                    return;
                }
                for (PointF point : mPoints) {
                    point.x = calculateCalculateX(point.x);
                    point.y = calculateCalculateY(point.y);
                }
                mWidth = getWidth();
                mHeight = getHeight();
                updateInvalidate();
            }
        });
        requestLayout();
    }

    // 修改中心点
    public void updateCenter(float x, float y) {
        PointF oldPointF = new PointF(mCenterPointF.x, mCenterPointF.y);
        mCenterPointF.set(x, y);
        for (PointF point : mPoints) {
            calculateCenterChangePointF(point, oldPointF, mCenterPointF);
        }
        for (StepBean stepBean : mBackStack) {
            PointF point = stepBean.getPoint();
            if (point != null) {
                calculateCenterChangePointF(point, oldPointF, mCenterPointF);
            }
        }
        updateInvalidate();
    }

    public void updateCenterTo(float x, float y) {
        updateCenter(mCenterPointF.x + x, mCenterPointF.y + y);
    }

    private void calculateCenterChangePointF(PointF changePointF, PointF oldCenterPointF, PointF newCenterPoint) {
        changePointF.x = changePointF.x - (oldCenterPointF.x - newCenterPoint.x);
        changePointF.y = changePointF.y - (oldCenterPointF.y - newCenterPoint.y);
    }

    /**
     * @param newScalePerPixel 新的像素规模
     */
    public void updateZoom(float newScalePerPixel) {
        if (mOldScalePerPixel == 0.0) {
            return;
        }
        float zoomRatio = mOldScalePerPixel / newScalePerPixel;
        mOldScalePerPixel = newScalePerPixel;

        for (PointF point : mPoints) {
            updatePointFZoom(point, zoomRatio);
        }
        for (StepBean stepBean : mBackStack) {
            PointF pointF = stepBean.getPoint();
            if (pointF != null) {
                updatePointFZoom(pointF, zoomRatio);
            }
        }
        updateInvalidate();
    }

    public float getOldScalePerPixel() {
        return mOldScalePerPixel;
    }

    private void updatePointFZoom(PointF point, float zoomRatio) {
        // x
        float differenceX = Math.abs(mCenterPointF.x - point.x) * zoomRatio;
        if (point.x > mCenterPointF.x) {
            point.x = mCenterPointF.x + differenceX;
        } else {
            point.x = mCenterPointF.x - differenceX;
        }
        // y
        float differenceY = Math.abs(mCenterPointF.y - point.y) * zoomRatio;
        if (point.y > mCenterPointF.y) {
            point.y = mCenterPointF.y + differenceY;
        } else {
            point.y = mCenterPointF.y - differenceY;
        }
    }

    public PointF getCenterPointF() {
        return mCenterPointF;
    }

    public float getZoom() {
        return mZoom;
    }

    private float calculateCalculateY(float y) {
        float centerY = getHeight() / 2f;
        float oldCenterY = mHeight / 2f;
        if (centerY == 0) return y;
        if (oldCenterY == 0) return y;
        if (y == centerY) {
            return y;
        } else if (y > centerY) {
            return centerY + (y - oldCenterY) / (oldCenterY / centerY);
        } else {
            return centerY - (oldCenterY - y) / (oldCenterY / centerY);
        }
    }

    private float calculateCalculateX(float x) {
        float centerX = getWidth() / 2f;
        float oldCenterX = mWidth / 2f;
        if (centerX == 0) return x;
        if (oldCenterX == 0) return x;
        if (x == centerX) {
            return x;
        } else if (x > centerX) {
            return centerX + (x - oldCenterX) / (oldCenterX / centerX);
        } else {
            return centerX - (oldCenterX - x) / (oldCenterX / centerX);
        }
    }

    /**
     * 对应的步进实体bean
     */
    class StepBean {

        /**
         * 对应的type值
         */
        // 操作的点
        static final int OPERATION_POINT = 1;
        // 操作的线
        static final int OPERATION_LINE = 2;

        // 记忆的index值
        private int index;
        // 记忆的点的值
        private PointF point;
        // 操作方式的type
        private int operationType;

        public int getIndex() {
            return index;
        }


        public void setIndex(int index) {
            this.index = index;
        }

        public int getOperationType() {
            return operationType;
        }

        public void setOperationType(int operationType) {
            this.operationType = operationType;
        }

        public PointF getPoint() {
            return point;
        }

        public void setPoint(PointF point) {
            this.point = point;
        }

        @Override
        public String toString() {
            return "StepBean{" +
                    "index=" + index +
                    ", point=" + point +
                    ", operationType=" + operationType +
                    '}';
        }
    }
}
